Skip to content

Add three-tier discovery system with UDP broadcast and manual peer entry fallbacks#37

Merged
infinityabundance merged 6 commits intomainfrom
copilot/add-peer-discovery-fallback
Feb 13, 2026
Merged

Add three-tier discovery system with UDP broadcast and manual peer entry fallbacks#37
infinityabundance merged 6 commits intomainfrom
copilot/add-peer-discovery-fallback

Conversation

Copy link
Contributor

Copilot AI commented Feb 13, 2026

Summary

Peer discovery fails completely when Avahi is unavailable (daemon down, not installed, or isolated networks). Implements three-tier discovery with automatic fallback: mDNS/Avahi → UDP broadcast → manual entry.

Details

  • New feature

What changed?

Tier 2: UDP Broadcast Discovery (src/discovery_broadcast.c, 238 lines)

  • Broadcasts discovery packets on port 5555 when mDNS unavailable
  • Automatic interface detection via getifaddrs()
  • Packet format: magic header + hostname + port + RootStream code
  • ~1-2s discovery time on local subnet

Tier 3: Manual Peer Entry (src/discovery_manual.c, 215 lines)

  • CLI options: --peer-add IP:PORT, --peer-code CODE, --peer-list
  • Parses IP:port and hostname:port formats
  • Peer history (32 entries, FIFO) for quick reconnection
  • DNS resolution for hostnames

Fallback Logic (src/discovery.c)

  • Tries mDNS, falls back to broadcast on failure
  • Non-fatal errors - manual entry always available
  • Broadcast timeout: 1000ms (configurable constant)

Header Changes (include/rootstream.h)

  • peer_history_entry_t structure (hostname, address, port, code)
  • Added to rootstream_ctx_t as peer_history_entries[32]
  • Function declarations for new discovery APIs

Security

  • Replaced unsafe strcpy() with strncpy() in address parsing
  • Proper null termination on all string operations
  • Bounds checking on hostname length

Rationale

  • Resilience: No single point of failure in peer discovery
  • Simplicity: Falls back automatically, no user intervention required
  • Linux-native: UDP broadcast works on any Linux system without dependencies
  • Latency: Broadcast discovery adds minimal overhead (~1s) only when mDNS fails

Testing

Describe how you tested this change:

  • Built successfully (make HEADLESS=1)
  • Tested CLI options (--peer-add, --peer-list)
  • Verified address parsing (IP:port, hostname:port)
  • Code review completed (4 issues addressed)
  • Security scan completed (strcpy → strncpy)
  • Tested on:
    • Distro: Ubuntu 24.04
    • Kernel: Linux 6.x
    • GPU & driver: N/A (headless build)

Notes

  • Potential impact on latency or resource usage: Minimal - broadcast only tried when mDNS fails, adds ~1s discovery time
  • Any follow-up work needed:
    • Persist peer history to disk (currently in-memory only)
    • IPv6 support for broadcast discovery
    • Integration tests with multiple peers on actual network
Original prompt

PHASE 5: Discovery Resilience & Peer Finding

Current State

  • src/discovery.c - mDNS/Avahi discovery (fully implemented)
  • src/network.c - Core networking with peer management
  • Missing: UDP broadcast fallback when mDNS unavailable
  • Missing: Manual peer entry/code input system
  • Missing: Peer history/favorites for quick reconnect
  • Missing: Local IP detection and advertisement

Problem

Currently in discovery:

  • mDNS/Avahi is the ONLY way to find peers
  • If Avahi service is down or not installed, peer discovery fails completely
  • No fallback mechanism for finding peers
  • Manual peer entry requires knowing IP address (not user-friendly)
  • No persistence of previously seen peers

On systems with:

  • No Avahi daemon running
  • Avahi disabled/crashed
  • Isolated networks where mDNS doesn't work
  • Users who prefer manual entry

Peer discovery becomes impossible.

Solution: Three-Tier Discovery with Manual Fallback

Tier 1: mDNS/Avahi (Primary)

  • File: src/discovery.c (already exists)
  • Status: ✅ Complete
  • Method: Avahi service discovery
  • Performance: Instant, automatic
  • Availability: Most desktop Linux systems

Tier 2: UDP Broadcast Discovery (New)

  • File: src/discovery_broadcast.c (NEW)
  • Method: Broadcast "RootStream?" on local subnet, listen for responses
  • Performance: ~1-2 seconds to discover
  • Availability: Works on any LAN without Avahi

Tier 3: Manual Peer Entry (New)

  • File: src/discovery_manual.c (NEW)
  • Method: User manually enters IP:port or hostname
  • Performance: Instant (no search needed)
  • Availability: Always (user can always enter an address)

Implementation

File 1: src/discovery_broadcast.c - UDP Broadcast Discovery

/*
 * discovery_broadcast.c - UDP broadcast peer discovery
 * 
 * Falls back to broadcast when mDNS unavailable.
 * Broadcasts a discovery packet on local subnet and waits for responses.
 * Works on any LAN without requiring Avahi.
 */

#include "../include/rootstream.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <poll.h>

#define DISCOVERY_BROADCAST_PORT 5555
#define DISCOVERY_MAGIC "ROOTSTREAM_DISCOVER"

typedef struct {
    uint8_t magic[20];        /* "ROOTSTREAM_DISCOVER" */
    uint32_t version;         /* Protocol version */
    char hostname[256];       /* Sender hostname */
    uint16_t listen_port;     /* Port peer is listening on */
    char rootstream_code[32]; /* User's RootStream code */
} discovery_broadcast_packet_t;

/*
 * Get local IP address for broadcast
 */
static int get_local_ip(char *ip_buf, size_t ip_len, char *bcast_buf, size_t bcast_len) {
    struct ifaddrs *ifaddr, *ifa;
    int ret = -1;

    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        return -1;
    }

    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL) continue;

        /* Skip loopback and non-IPv4 */
        if (ifa->ifa_addr->sa_family != AF_INET) continue;
        if (strcmp(ifa->ifa_name, "lo") == 0) continue;

        struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr;
        struct sockaddr_in *broadcast = (struct sockaddr_in *)ifa->ifa_broadaddr;

        inet_ntop(AF_INET, &sin->sin_addr, ip_buf, ip_len);
        if (broadcast) {
            inet_ntop(AF_INET, &broadcast->sin_addr, bcast_buf, bcast_len);
        } else {
            snprintf(bcast_buf, bcast_len, "255.255.255.255");
        }

        printf("✓ Using interface %s (%s, broadcast %s)\n", 
               ifa->ifa_name, ip_buf, bcast_buf);
        ret = 0;
        break;
    }

    freeifaddrs(ifaddr);
    return ret;
}

/*
 * Broadcast discovery query
 */
int discovery_broadcast_announce(rootstream_ctx_t *ctx) {
    if (!ctx) return -1;

    char local_ip[INET_ADDRSTRLEN];
    char broadcast_ip[INET_ADDRSTRLEN];

    if (get_local_ip(local_ip, sizeof(local_ip), 
                     broadcast_ip, sizeof(broadcast_ip)) < 0) {
        fprintf(stderr, "ERROR: Cannot determine local IP\n");
        return -1;
    }

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0) {
        perror("socket");
        return -1;
    }

    /* Enable broadcast */
    int broadcast_flag = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast_flag, 
                   sizeof(broadcast_flag)) < 0) {
        perror("setsockopt SO_BROADCAST");
        close(sock);
        return -1;
    }

    /* Create announcement packet */
    discovery_broadcast_packet_t pkt;
    memcpy(pkt.magic, DISCOVERY_MAGIC, strlen(DISCOVERY_MAGIC));
    pkt.version = PROTOCOL_VERSION;
    gethostname(pkt.hostname, sizeof(pkt.hostname));
    pkt.listen_port = ctx->network.listen_port;
    strncpy(pkt.rootstream_code, ctx->rootstream_code, sizeof(pkt.rootstream_code) - 1);

    str...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Copilot AI and others added 5 commits February 13, 2026 02:13
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Co-authored-by: infinityabundance <255699974+infinityabundance@users.noreply.github.com>
Copilot AI changed the title [WIP] Add UDP broadcast fallback and manual peer entry for discovery Add three-tier discovery system with UDP broadcast and manual peer entry fallbacks Feb 13, 2026
@infinityabundance infinityabundance marked this pull request as ready for review February 13, 2026 02:29
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@infinityabundance infinityabundance merged commit fb75209 into main Feb 13, 2026
4 of 9 checks passed
@infinityabundance infinityabundance deleted the copilot/add-peer-discovery-fallback branch February 19, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants